查看原文
其他

使用 R 语言绘制中国乡镇地图及根据经纬度判断所处的乡镇

RStata RStata 2024-04-19

为了让大家更好的理解本文内容,欢迎各位培训班会员参加明晚 8 点 的直播课:「使用 R 语言绘制中国乡镇地图及根据经纬度判断所处的乡镇」


在之前的课程中,我给大家分享过使用 R 语言绘制省市区县地图的方法:

  • 使用 R 语言绘制历年中国省市区县地图(小地图版本+长版):https://rstata.duanshu.com/#/brief/course/379d19770956478ebc4d919910fca74c

另外也给大家分享过使用 R 语言进行地理编码以及根据经纬度判断所处省市区县的方法:

  • 使用 R 语言进行地理编码:地址解析经纬度、坐标转换 & 根据经纬度判断所处的省市区县:https://rstata.duanshu.com/#/brief/course/6f8633b486ed49e398c486d0d9f0f59a

乡镇地图的绘制

今天我们再来补充讲解下如何绘制乡镇地图以及如何根据经纬度判断所处的乡镇。

附件中的 town4326 文件夹是 WGS84 坐标系下的乡镇地图数据;town_mini 文件夹是经过编辑后的小地图版本的乡镇地图数据;town_long 则是长版本的。

town_mini 和 town_long 都是“+proj=aea +lat_0=0 +lon_0=105 +lat_1=25 +lat_2=47 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs”坐标系的,也就是和之前的地图数据一样。

首先加载相关 R 包并读取数据:

library(tidyverse)
library(sf)

# 中国地图通常使用这样的坐标系
mycrs <- "+proj=aea +lat_0=0 +lon_0=105 +lat_1=25 +lat_2=47 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs" 

# 小地图版本
# 读取编辑后的乡镇地图数据
read_sf("town_mini/town_mini.shp") -> townmini 

# 线条数据可以使用之前课程中提供的省市区县的:
read_sf("chinaprov2021mini/chinaprov2021mini_line.shp") %>% 
  filter(class %in% c("九段线""海岸线""小地图框格")) %>% 
  select(class) -> provlinemap

# 读取绘图数据
haven::read_dta("2019年各乡镇平均人口密度.dta") %>% 
  rename(townname = 乡镇, towncode = 乡镇代码) -> df 

df

#> # A tibble: 43,366 × 3
#>    townname     towncode       mean
#>    <chr>        <chr>         <dbl>
#>  1 一元街办事处 420102003000 9076. 
#>  2 一六镇       431022108000  295. 
#>  3 一六镇       440232103000   89.2
#>  4 一农场       130209401000  346. 
#>  5 一品街道     500113008000  557. 
#>  6 一市镇       330226104000  426. 
#>  7 一平垣乡     141002203000  677. 
#>  8 一平浪镇     532331105000   72.9
#>  9 一座营镇     220581109000  252. 
#> 10 一心乡       230624200000   45.7
#> # ℹ 43,356 more rows

然后就可以绘图了,这部分细节较多,建议结合视频讲解学习:

library(ggspatial)
library(ggnewscale)
townmini %>% 
  left_join(df) %>% 
  mutate(mean = if_else(is.na(mean), -1, mean)) %>% 
  mutate(group = cut(mean, breaks = c(-1, -0.168125197312
                                      458663136050000),
                     include.lowest = T,
                     labels = c("无数据""<=68"
                                "68~125""125~197",
                                "197~312""312~458",
                                "458~663""663~1360",
                                ">1360"))) -> mapdata 

mapdata %>% 
  ggplot() + 
  geom_sf(aes(fill = group), color = "gray", linewidth = 0.01) + 
  geom_sf(data = provlinemap, 
          aes(color = class, linewidth = class),
          show.legend = F) + 
  scale_color_manual(
    values = c("九段线" = "#A29AC4",
               "海岸线" = "#0055AA",
               "小地图框格" = "black",
               "省份" = "black")
  ) + 
  scale_linewidth_manual(
    values = c("九段线" = 0.6,
               "海岸线" = 0.3,
               "小地图框格" = 0.3,
               "省份" = 0.3)
  ) + 
  scico::scale_fill_scico_d(palette = "bamako"
                          name = "人口密度(人/m2)"
                          direction = -1) + 
  annotation_scale(location = "bl"
                   width_hint = 0.3
                   text_family = cnfont) + 
  labs(title = "2019 年各乡镇平均人口密度",
       subtitle = "数据爬取&绘制:微信公众号 RStata",
       caption = "数据来源:中国科学院资源环境科学与数据中心") + 
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank()) + 
  annotation_north_arrow(
    location = "tr"
    which_north = "false",
    pad_y = unit(0.1"cm"), 
    style = north_arrow_fancy_orienteering(
      text_family = cnfont 
    )
  ) -> p1 

ggsave("pic1.png", width = 10, height = 8.5, device = png)    

长版地图的绘制代码类似,数据换成长版的即可:


# 长版 
read_sf("town_long/town_long.shp") -> townlong 
townlong %>% 
  left_join(df) %>% 
  mutate(mean = if_else(is.na(mean), -1, mean)) %>% 
  mutate(group = cut(mean, breaks = c(-1, -0.168125197312
                                      458663136050000),
                     include.lowest = T,
                     labels = c("无数据""<=68"
                                "68~125""125~197",
                                "197~312""312~458",
                                "458~663""663~1360"
                                ">1360"))) -> mapdata 

# 线条 
read_sf("chinaprov2021long/chinaprov2021long_line.shp") %>% 
  filter(class %in% c("九段线""海岸线")) %>% 
  select(class) -> provlinemap 

ggplot(mapdata) + 
  geom_sf(aes(fill = group), color = "gray", linewidth = 0.01) + 
  geom_sf(data = provlinemap, 
          aes(color = class, linewidth = class),
          show.legend = F) + 
  scale_color_manual(
    values = c("九段线" = "#A29AC4",
               "海岸线" = "#0055AA")
  ) + 
  scale_linewidth_manual(
    values = c("九段线" = 0.6,
               "海岸线" = 0.3)
  ) + 
  scico::scale_fill_scico_d(palette = "bamako"
                            name = "人口密度(人/m2)"
                            direction = -1) + 
  annotation_scale(location = "bl"
                   width_hint = 0.3
                   text_family = cnfont) + 
  coord_sf(xlim = c(-2625585.82206964.7) * 1.2) + 
  labs(title = "2019 年各乡镇平均人口密度",
       subtitle = "数据爬取&绘制:微信公众号 RStata",
       caption = "数据来源:中国科学院资源环境科学与数据中心") + 
  theme(axis.title.x = element_blank(),
        axis.title.y = element_blank()) + 
  annotation_north_arrow(
    location = "tr"
    which_north = "false",
    pad_y = unit(0.1"cm"), 
    style = north_arrow_fancy_orienteering(
      text_family = cnfont
    ) 
  ) -> p2 

ggsave("pic2.png", width = 8, height = 7, device = png) 

根据经纬度判断所处的乡镇

首先读取乡镇矢量数据:

read_sf("town4326/town4326.shp") -> town 

工企样本.dta 是大概 1 万条工企数据的样本,含有经纬度变量,使用下面的代码就可以判断每个坐标点所处的乡镇了:

haven::read_dta("工企样本.dta") %>% 
  filter(!is.na(经度)) %>% 
  st_as_sf(coords = c("经度""纬度"), crs = 4326, remove = F) -> dfres 

如果数据量不大,可以直接用下面的代码判断:

# dfres %>% 
#   st_intersection(town) -> dfres 

数据量大的时候会非常慢,建议转换成 mycrs 坐标系再运算:

# 建议转换成 mycrs 坐标系再判断:
mycrs <- "+proj=aea +lat_0=0 +lon_0=105 +lat_1=25 +lat_2=47 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs" 
dfres %>% 
  st_transform(mycrs) %>% 
  st_intersection(st_transform(town, mycrs)) -> dfres2

dfres2 %>% 
  st_drop_geometry() 
#> # A tibble: 10,218 × 7
#>     group       gqid 企业名称                  经度  纬度 Name       code       
#>  *  <dbl>      <dbl> <chr>                    <dbl> <dbl> <chr>      <chr>      
#>  1 719089 1998169629 天津酿酒厂                117.  39.2 丁字沽街道 1201060040…
#>  2 721426 2008228990 大连水泥集团有限公司      122.  39.3 七顶山街道 2102130210…
#>  3 721426 2009042065 大连水泥集团有限公司      122.  39.3 七顶山街道 2102130210…
#>  4 721426 2014087208 大连水泥集团有限公司      122.  39.3 七顶山街道 2102130210…
#>  5 719068 1998169308 天津市神光新技术开发公司  117.  39.1 万兴街道   1201040050…
#>  6 718486 1999000502 北京金盾印刷厂            116.  39.9 万寿路街道 1101080010…
#>  7 718486 2004003087 北京金盾印刷厂            116.  39.9 万寿路街道 1101080010…
#>  8 718486 2007333827 北京金盾印刷厂            116.  39.9 万寿路街道 1101080010…
#>  9 718486 2003004268 北京金盾印刷厂            116.  39.9 万寿路街道 1101080010…
#> 10 718486 2013140022 北京金盾印刷厂            116.  39.9 万寿路街道 1101080010…
#> # ℹ 10,208 more rows

直播信息

为了让大家更好的理解本文内容,欢迎各位培训班会员参加明晚 8 点的直播课:「使用 R 语言绘制中国乡镇地图及根据经纬度判断所处的乡镇」

  1. 直播地址:腾讯会议(需要报名 RStata 培训班参加)
  2. 讲义材料:需要购买 RStata 名师讲堂会员,详情可阅读:一起来学习 R 语言和 Stata 啦!学习过程中遇到的问题也可以随时提问!

更多关于 RStata 会员的更多信息可添加微信号 r_stata 咨询:

附件下载(点击文末的阅读原文即可跳转):

https://rstata.duanshu.com/#/brief/course/eea9db6b6cd3420e85ab4d8dbc9b4eda


继续滑动看下一个
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存